/*
The MIT License (MIT)

Copyright (c) 2013 Mike Dapiran, Brian May, Richard Pospesel, and Bert Wierenga

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#pragma once

namespace Hogshead
{
	namespace Common
	{
		/**
		* Random is a psuedo-random number generator (specifically a Linear Congruential Generator)
		* which can be seeded and provides both evenly distributed and gaussian distributed pseudo-
		* random numbers.  Random implements the PRNG used by Borland Delphi which uses the following 
		* parameters:
		* m = 2^32
		* a = 1664525
		* c = 1013904223
		*
		* For more details go to: 
		* http://en.wikipedia.org/wiki/Linear_congruential_generator
		*
		* The Gaussian random number generator uses the algorithm implemented by the JVM and details
		* can be found here: 
		* http://download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/api/java/util/Random.html#nextGaussian()
		*/
		class Random
		{
		public:
			/*
			* Default constructor is seeded with the current time from time(NULL);
			*/
			Random();
			/*
			* Constructor accepts a long long seed.
			*/
			Random(unsigned long long in_seed);

			/*
			* Returns a evenly distributed pseudo random integer between and including two integers.
			* @param in_min The minimum value to be returned.
			* @param in_max The maximal value to be returned.
			* @return A pseudo random number ranging from in_min to in_max.
			*/
			int nextInt(int in_min, int in_max);
			
			/*
			* Returns an evenly distributed pseudo-random flaot on 0.0, 1.0.
			* @return A pseudo-random number on 0.0, 1.0.
			*/
			float nextFloat();
			
			/*
			* Returns a evenly distributed pseudo-random double from in_min to in_max.
			* @param in_min The minimum value possible to return.
			* @param in_max The maximum value possible to return.
			* @return An evenly distributed pseudo-random float from in_min to in_max.
			*/
			float nextFloat(float in_min, float in_max);

			/*
			* Returns a evenly distributed pseudo random double from 0.0 to 1.0.
			* @return A pseudo-random number on 0.0,1.0.
			*/
			double nextDouble();
			/*
			* Returns a evenly distributed pseudo-random double from in_min to in_max.
			* @param in_min The minimum value possible to return.
			* @param in_max The maximum value possible to return.
			* @return An evenly distributed pseudo-random double from in_min to in_max.
			*/
			double nextDouble(double in_min, double in_max);

			/*
			* Returns a Normally distributed pseudo-random number (mean 0.0, standard deviation 1.0).
			* @return A normally distributed pseudo-random number.
			*/
			double nextGaussian();
			/*
			* Returns a Gaussian distributed pseudo-random number from a distribution with the specified mean
			* and standard deviation.
			* @param in_mean The mean of the distribution to draw the result from.
			* @param in_standard_deviation The standard deviation of the distribution to draw the result from.
			* @return Gaussian pseudo-random number with the specified mean and standard deviation.
			*/
			double nextGaussian(double in_mean, double in_standard_deviation);

			/*
			* Set the seed of this Random object.
			* @param in_seed The seed to use.
			*/
			void setSeed(long long in_seed);
		private:

			unsigned int next();

			const static long long _m = ((long long)1) << 32;
			const static int _a = 1664525;
			const static int _c = 1013904223;
			unsigned long long _val;

			double _nextNextGaussian;
			bool _haveNextNextGaussian;
			
		};
	}
}